package src.main.java;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;



/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ws
 * Date: 2023-12-27
 * Time: 14:18
 */
public class T7 {

    public static void main(String[] args) throws InterruptedException {
//        stackOverflowError();
        /** ERROR
         * Exception in thread "main" java.lang.StackOverflowError
         * 递归调用，不断开辟栈帧，栈空间耗尽
         * OOM（OutOfMemoryError），也是 ERROR
         *
         */


        outOfMemoryError();
        /**
         * 一
         * 对象过多
         * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
         *
         * 二
         * 大对象太大
         * Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
         *
         * 三
         * // GC 次数过多时间占用过长，且效果不好
         * VM Option 参数：-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
         * GC回收时间过长时会抛出 java.lang.OutOfMemoryError: GC overhead limit exceeded
         * 过长的定义是，超过98%的时间用来做GC并且回收了不到2%的堆内存连续多次GC 都只回收了不到2%的极端情况下才会抛出。
         * 假如不抛出GC overhead limit错误会发生什么情况呢?那就是GC清理的这么点内存很快会再次填满，迫使GC再次执行．这样就形戍恶性循环，
         * CPU使用率一直是100%,而GC却没有任何成果
         *
         * 四
         * 配置参数:
         * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
         *
         * 故障现象
         *Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
         *
         * 导致原因:
         * 写 NIO程序经常使用 ByteBuffer来读取或者写入数据，这是一种基于通道(Channel)与缓冲区(Buffer)的I/0方式。
         * 它可以使用 Native函数库直接分配堆外内存，然后通过一个存储在Java堆里面的 DirectByteBuffer对象作为这块内存的引用进行操作。
         * 这样能在一些场景中显著提高性能，因为避兔了在 Java堆和 Native堆中来回复制数据。
         *
         * ByteBuffer.allocate(capability)第一种方式是分配JVM维内存，属于GC管辖范围，由于需要拷贝所以速度相对较慢
         *
         * ByteBuffer.allocateDirect(capability)第2种方式是分配OS本地内存，不属子GC管辖范围，
         * 由于不需要内存拷贝所以速度相对较快。但如果不断分配本地内存，堆内存很少使用，
         * 那么JVM就不需要执行Gc，DirectByteBuffer对象们就不会被回收
         *
         * 这时候堆内存充足，但本地内存可能已经使用光了，再次尝试分配本地内存就会出现QutOfMemoryError，那程序就直接崩溃了。
         */



        // 五

        /**
         * 高并发请求服务器时,经常出现如下异常: java.lang.OutOfMemoryError: unable to create new native thread
         * 准确的讲该 native thread异常与对应的平台有关
         *
         * 导致原因:
         * 1. 你的应用创建了太多线程了，一个应用进程创建多个线程,超过系统承载极限
         * 2. 你的服务器并不允许你的应用程序创建这么多线程,Linux系统默认允许单个进程可以创建的线程数是1024个,
         *    你的应用创建超过这个数量,就会报java.Lang. OutOfMemoryError: unable to create new native thread
         *
         * 解决办法:
         * 1.想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代码将线程数降到最低
         * 3.对于有的应用,确实需要创建很多线程,远超过Linux系统的默认1024个线程的限制，可以通过修改Linux服务器配置,扩大Linux默认限制
         *
         */



        // 六
        /**
         * JVM参数
         * -XX:MetaspaceSize=18m -XX:MaxMetaspaceSize=18m
         *
         *
         * java.lang.OutOfMemoryError: Metaspace
         *
         * Java 8及之后的版本使用 Metaspace 来替代永久代。
         * Metaspace 是方法区在 HotSpot中的实现，它与持久代最大的区别在于:Metaspace并不在虚拟机内存中而是使用本地内存
         * 也即在java8中,classe metadata(the virtual machines internal presentation of Java class)
         * 被存储在叫做 Metaspace 的 native memory
         * I
         * 永久代(java8后被原空间 Metaspace取代了）存放了以下信息:
         * 虚拟机加载的类信息
         * 常量池
         * 静态变量
         * 即时编译后的代码
         * 模拟 Metaspace空间溢出，我们不断生成类往元空间灌，类占据的空间总是会超过 Metaspace指定的空间大小的
         *
         *
         */


        // 模拟计数多少次后发生异常
        int i = 0;
        try {
            while (true) {
                i++;
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMTest.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        return methodProxy.invokeSuper(o, args);
                    }
                });
                enhancer.create();
            }
        } catch (Throwable e) {
            System.out.println("===================第多少次后发生了异常" + i);
            e.printStackTrace();
        }


        }

    private static void outOfMemoryError() throws InterruptedException {
        // 一
        // 不断创建新对象
//        String str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
//        while (true) {
//            // 不断创建新的对象
//            str += str + new Random().nextInt(Integer.MAX_VALUE) + new Random().nextInt(Integer.MAX_VALUE);
//            // 加入到常量池，即堆空间中
//            str.intern();
//        }


        // 二
        // 大对象

//        byte[] arr = new byte[Integer.MAX_VALUE * Integer.MAX_VALUE * Integer.MAX_VALUE * Integer.MAX_VALUE * Integer.MAX_VALUE * Integer.MAX_VALUE * Integer.MAX_VALUE];


//    三
//        int i = 0;
//        List<String> list = new ArrayList<>();
//        try {
//            while (true) {
//                list.add(String.valueOf(++i).intern());
//            }
//        } catch (Throwable e) {
//            System.out.println("*********************" + i);
//            e.printStackTrace();
//            throw e;
//        }


        // 四
//        System.out.println("配置的 maxDirectMemory" + (sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024) + "MB");
//        Thread.sleep(3000);
//        // -XX:MaxDirectMemorySize=5m
//        // 我们配置为 5MB，实际使用 6MB
//        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);


        // 五
        // 不要运行，会死机
//        for (int i = 0; ; i++) {
//            System.out.println("==========================i" + i);
//            new Thread(() -> {
//                try {
//                    Thread.sleep(Integer.MAX_VALUE);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }, "" + i).start();
//        }


    }


    // 六
    static class OOMTest {

    }

    private static void stackOverflowError() {
        stackOverflowError();
    }
}
